/***
    Amrita ITEWS - Copyright (C) 2009 Amrita Vishwa Vidyapeetham, Amritapuri.
                                       (http://www.amrita.edu)
    ***************************************************************************
    This file is part of the Amrita ITEWS distribution.
    Amrita ITEWS is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License (version 2) as published
    by the Free Software Foundation AND MODIFIED BY the Amrita ITEWS exception.
    ***NOTE*** The exception to the GPL is included to allow you to distribute
    a combined work that includes Amrita ITEWS without being obliged to provide
    the source code for proprietary components outside of the Amrita ITEWS
    software. Amrita ITEWS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
    Public License for more details. You should have received a copy of the GNU
    General Public License and the Amrita ITEWS license exception along with
    Amrita ITEWS if not then it can be viewed here:
    http://itews.amrita.ac.in/license.html.

    Note: This software is closely coupled to FreeRTOS and UIP and hence the
    licensing terms of FreeRTOS and UIP would apply.


    Documentation, latest information, license and contact details are at:
    http://itews.amrita.ac.in/


    Amrita ITEWS source code can be found at:
    http://code.google.com/p/itews


    The initial code-base of Amrita ITEWS was developed by Amrita Vishwa
    Vidyapeetham as part of the project titled,"Intelligent & Interactive
    Telematics using Emerging Wireless Technologies for Transport Systems
    (ITEWS)" sponsored by Technology Information Forecasting and Assessment
    Council (TIFAC), India.
***/

/*
UIP running as a task under FreeRTOS.  Developed as part of ITEWS project by Amrita Research Labs.
*/
#include <FreeRTOS.h>
#include <uip.h>
#include <uip_arp.h>
#include <enc28j60.h>
#include <timer.h>
#include <klog.h>
#include <uip_task.h>
#include <sys.h>
#include <task.h>
#include <mutex_api.h>
#include <socket.h>

#define BUF ((struct uip_eth_hdr *)&uip_buf[0])

volatile int uip_task_powerdown = 0;
volatile int uip_task_eth_powerdown = 0;

void uip_task_appcall();

void uip_task_init(const uint8 *ipbuf, const uint8 *netmask, const uint8 *mac, const uint8 *drbuf)
{
    uip_ipaddr_t ipaddr;

    uip_init();
    uip_arp_init();

    uip_ipaddr(ipaddr, ipbuf[0], ipbuf[1], ipbuf[2], ipbuf[3]);
    uip_sethostaddr(ipaddr);

    uip_ipaddr(ipaddr, drbuf[0], drbuf[1], drbuf[2], drbuf[3]);
    uip_setdraddr(ipaddr);

    uip_ipaddr(ipaddr, netmask[0], netmask[1], netmask[2], netmask[3]);
    uip_setnetmask(ipaddr);

    uip_ethaddr.addr[0] = mac[0];
    uip_ethaddr.addr[1] = mac[1];
    uip_ethaddr.addr[2] = mac[2];
    uip_ethaddr.addr[3] = mac[3];
    uip_ethaddr.addr[4] = mac[4];
    uip_ethaddr.addr[5] = mac[5];

}


void uip_task(void *p)
{
    int i;
    struct timer periodic_timer, arp_timer;

    enc28j60_open();

    timer_set(&periodic_timer, CLOCK_SECOND / 2);
    timer_set(&arp_timer, CLOCK_SECOND * 10);


    while(1)
    {
        if(uip_task_powerdown)
        {
            if(!uip_task_eth_powerdown)
            {
                enc28j60_powerdown();
                uip_task_eth_powerdown = 1;
            }
            sys_msleep(UIP_TASK_YIELD_TIME_MS);
            continue;
        }else
        {
            if(uip_task_eth_powerdown)
            {
                enc28j60_powerup();
                uip_task_eth_powerdown = 0;
            }
        }

        socket_process_pending_lists_();

        uip_len = enc28j60_read();

        if(uip_len > 0)
        {
            if(BUF->type == htons(UIP_ETHTYPE_IP))
            {
                uip_input();
                /* If the above function invocation resulted in data that
                should be sent out on the network, the global variable
                uip_len is set to a value > 0. */
                if(uip_len > 0)
                {
                    uip_arp_out();
                    enc28j60_write();
                }
            }
            else if(BUF->type == htons(UIP_ETHTYPE_ARP))
            {
                uip_arp_arpin();
                /* If the above function invocation resulted in data that
                should be sent out on the network, the global variable
                uip_len is set to a value > 0. */
                if(uip_len > 0)
                {
                    enc28j60_write();
                }
            }

        }

        if(timer_expired(&periodic_timer))
        {
            timer_reset(&periodic_timer);
            for(i = 0; i < UIP_CONNS; i++)
            {
                uip_periodic(i);
                /* If the above function invocation resulted in data that
                should be sent out on the network, the global variable
                uip_len is set to a value > 0. */
                if(uip_len > 0)
                {
                    uip_arp_out();
                    enc28j60_write();
                }
            }

#if UIP_UDP
            for(i = 0; i < UIP_UDP_CONNS; i++)
            {
                uip_udp_periodic(i);
                /* If the above function invocation resulted in data that
                should be sent out on the network, the global variable
                uip_len is set to a value > 0. */
                if(uip_len > 0)
                {
                    uip_arp_out();
                    enc28j60_write();
                }
            }
#endif /* UIP_UDP */
            /* Call the ARP timer function every 10 seconds. */
            if(timer_expired(&arp_timer))
            {
                timer_reset(&arp_timer);
                uip_arp_timer();
            }
        }

        //yield so that other tasks can do something
        sys_msleep(UIP_TASK_YIELD_TIME_MS);
    }

}


void uip_log(char *msg)
{
    klog_info("UIP: %s", msg);
}


void uip_task_appcall()
{
    if(uip_connected())
    {
        socket_process_new_connect_();
    }else
    if(uip_poll())
    {
        if((!uip_udpconnection()) && uip_stopped(uip_conn))
        {
            socket_process_try_restart_();
        }

        socket_process_write_();
    }else
    if(uip_newdata())
    {
        socket_process_new_data_();
        socket_process_write_();
    }else
    if(uip_aborted() || uip_closed())
    {
        socket_process_close_();
    }else
    if(uip_timedout())
    {
        socket_process_timeout_();
        uip_close();
    }else
    if(uip_acked() || uip_rexmit())
    {
        socket_process_write_();
    }
}

